package org.geometerplus.fbreader.fbreader;

import android.app.Activity;

import com.shwread.android.qysw.activity.ReadingBookActivity;

import org.geometerplus.fbreader.book.Book;
import org.geometerplus.fbreader.book.BookEvent;
import org.geometerplus.fbreader.book.Bookmark;
import org.geometerplus.fbreader.book.BookmarkQuery;
import org.geometerplus.fbreader.book.IBookCollection;
import org.geometerplus.fbreader.bookmodel.BookModel;
import org.geometerplus.fbreader.bookmodel.BookReadingException;
import org.geometerplus.fbreader.bookmodel.TOCTree;
import org.geometerplus.fbreader.fbreader.options.FooterOptions;
import org.geometerplus.fbreader.fbreader.options.HeaderOptions;
import org.geometerplus.fbreader.fbreader.options.PageTurningOptions;
import org.geometerplus.zlibrary.core.application.ZLApplication;
import org.geometerplus.zlibrary.core.application.ZLKeyBindings;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.options.ZLBooleanOption;
import org.geometerplus.zlibrary.core.options.ZLColorOption;
import org.geometerplus.zlibrary.core.options.ZLEnumOption;
import org.geometerplus.zlibrary.core.options.ZLIntegerRangeOption;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.text.hyphenation.ZLTextHyphenator;
import org.geometerplus.zlibrary.text.model.ZLTextModel;
import org.geometerplus.zlibrary.text.view.ZLTextFixedPosition;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
import org.geometerplus.zlibrary.text.view.ZLTextView;
import org.geometerplus.zlibrary.text.view.ZLTextWordCursor;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary;

import java.util.Collections;
import java.util.Date;
import java.util.List;

import logic.bean.TOCItem;
import logic.util.BookSetting;
import logic.util.Util;

/**
 * 主要逻辑控制类
 *
 * @author luhuhai
 */
public final class FBReaderApp
        extends
        ZLApplication {
    public final ZLBooleanOption AllowScreenBrightnessAdjustmentOption = new ZLBooleanOption("LookNFeel", "AllowScreenBrightnessAdjustment", true);
    public final ZLStringOption TextSearchPatternOption = new ZLStringOption("TextSearch", "Pattern", "");

    public final ZLBooleanOption UseSeparateBindingsOption = new ZLBooleanOption("KeysOptions", "UseSeparateBindings", false);

    public final ZLBooleanOption EnableDoubleTapOption = new ZLBooleanOption("Options", "EnableDoubleTap", false);
    public final ZLBooleanOption NavigateAllWordsOption = new ZLBooleanOption("Options", "NavigateAllWords", false);

    public static enum WordTappingAction {
        doNothing, selectSingleWord, startSelecting, openDictionary
    }

    public final ZLEnumOption<WordTappingAction> WordTappingActionOption = new ZLEnumOption<WordTappingAction>("Options", "WordTappingAction", WordTappingAction.startSelecting);

    public final ZLColorOption ImageViewBackgroundOption = new ZLColorOption("Colors", "ImageViewBackground", new ZLColor(255, 255, 255));
    public final ZLEnumOption<FBView.ImageFitting> FitImagesToScreenOption = new ZLEnumOption<FBView.ImageFitting>("Options", "FitImagesToScreen", FBView.ImageFitting.covers);

    public static enum ImageTappingAction {
        doNothing, selectImage, openImageView
    }

    public final ZLEnumOption<ImageTappingAction> ImageTappingActionOption = new ZLEnumOption<ImageTappingAction>("Options", "ImageTappingAction", ImageTappingAction.openImageView);

    public final ZLBooleanOption TwoColumnViewOption;
    public final ZLIntegerRangeOption LeftMarginOption;
    public final ZLIntegerRangeOption RightMarginOption;
    public final ZLIntegerRangeOption TopMarginOption;
    public final ZLIntegerRangeOption BottomMarginOption;
    public final ZLIntegerRangeOption SpaceBetweenColumnsOption;
    public final ZLIntegerRangeOption FooterHeightOption;
    public final ZLIntegerRangeOption HeaderHeightOption;

    {
        final int dpi = ZLibrary.Instance().getDisplayDPI();
        //#debug
        System.out.println("~~~~~~~~~~~~dpi:" + dpi);
        final int x = ZLibrary.Instance().getPixelWidth();
        final int y = ZLibrary.Instance().getPixelHeight();
        final int horMargin = Math.min(dpi / 5, Math.min(x, y) / 20);
        BookSetting.SetMargin(horMargin);
        System.out.println("边距   " + horMargin);
        TwoColumnViewOption = new ZLBooleanOption("Options", "TwoColumnView", x * x + y * y >= 42 * dpi * dpi);
        LeftMarginOption = new ZLIntegerRangeOption("Options", "LeftMargin", 0, 100, horMargin);
        RightMarginOption = new ZLIntegerRangeOption("Options", "RightMargin", 0, 100, horMargin);
        TopMarginOption = new ZLIntegerRangeOption("Options", "TopMargin", 0, 100, 14);
        BottomMarginOption = new ZLIntegerRangeOption("Options", "BottomMargin", 0, 100, 14);
        SpaceBetweenColumnsOption = new ZLIntegerRangeOption("Options", "SpaceBetweenColumns", 0, 4 * horMargin, 2 * horMargin);
        FooterHeightOption = new ZLIntegerRangeOption("Options", "FooterHeight", 8, dpi / 5, dpi / 8);
        HeaderHeightOption = new ZLIntegerRangeOption("Options", "HeaderHeight", 30, dpi / 3, dpi / 5);
    }

    public final ZLIntegerRangeOption HeaderBarTypeOption = new ZLIntegerRangeOption("Options", "HeaderBarType", 0, 3, FBView.SCROLLBAR_SHOW_AS_HEADER);

    public final ZLIntegerRangeOption ScrollbarTypeOption = new ZLIntegerRangeOption("Options", "ScrollbarType", 0, 3, FBView.SCROLLBAR_SHOW_AS_FOOTER);

    final ZLStringOption ColorProfileOption = new ZLStringOption("Options", "ColorProfile", ColorProfile.DAY);

    public final PageTurningOptions PageTurningOptions = new PageTurningOptions();
    public final FooterOptions FooterOptions = new FooterOptions();
    public final HeaderOptions HeaderOptions = new HeaderOptions();

    public final ZLIntegerRangeOption brightOption = new ZLIntegerRangeOption("Options", "bright", 0, 100, 50);

    private final ZLKeyBindings myBindings = new ZLKeyBindings("Keys");

    public final FBView BookTextView;
    public final FBView FootnoteView;
    private String myFootnoteModelId;

    public volatile BookModel Model;

    private ZLTextPosition myJumpEndPosition;
    private Date myJumpTimeStamp;

    public final IBookCollection Collection;
    public String contentId;
    public String chapterId;
    public String chapterName;

    public boolean isMenuShowing;
    private ReadingBookActivity readerActivity;
    private boolean dontCloseLoading = false;
    private ZLTextPosition gotoPosition;


    public FBReaderApp(IBookCollection collection) {
        Collection = collection;

        collection.addListener(new IBookCollection.Listener() {
            public void onBookEvent(BookEvent event, Book book) {
                switch (event) {
                    case BookmarkStyleChanged:
                    case BookmarksUpdated:
                        if (Model != null && (book == null || book.equals(Model.Book))) {
                            if (BookTextView.getModel() != null) {
                                setBookmarkHighlightings(BookTextView, null);
                            }
                            if (FootnoteView.getModel() != null && myFootnoteModelId != null) {
                                setBookmarkHighlightings(FootnoteView, myFootnoteModelId);
                            }
                        }
                        break;
                    case Updated:
                        onBookUpdated(book);
                        break;
                }
            }

            public void onBuildEvent(IBookCollection.Status status) {
            }
        });

        addAction(ActionCode.INCREASE_FONT, new ChangeFontSizeAction(this, +2));
        addAction(ActionCode.DECREASE_FONT, new ChangeFontSizeAction(this, -2));

        addAction(ActionCode.FIND_NEXT, new FindNextAction(this));
        addAction(ActionCode.FIND_PREVIOUS, new FindPreviousAction(this));
        addAction(ActionCode.CLEAR_FIND_RESULTS, new ClearFindResultsAction(this));

        addAction(ActionCode.SELECTION_CLEAR, new SelectionClearAction(this));

        addAction(ActionCode.TURN_PAGE_FORWARD, new TurnPageAction(this, true));
        addAction(ActionCode.TURN_PAGE_BACK, new TurnPageAction(this, false));

        addAction(ActionCode.MOVE_CURSOR_UP, new MoveCursorAction(this, FBView.Direction.up));
        addAction(ActionCode.MOVE_CURSOR_DOWN, new MoveCursorAction(this, FBView.Direction.down));
        addAction(ActionCode.MOVE_CURSOR_LEFT, new MoveCursorAction(this, FBView.Direction.rightToLeft));
        addAction(ActionCode.MOVE_CURSOR_RIGHT, new MoveCursorAction(this, FBView.Direction.leftToRight));

        addAction(ActionCode.VOLUME_KEY_SCROLL_FORWARD, new VolumeKeyTurnPageAction(this, true));
        addAction(ActionCode.VOLUME_KEY_SCROLL_BACK, new VolumeKeyTurnPageAction(this, false));

        addAction(ActionCode.SWITCH_TO_DAY_PROFILE, new SwitchProfileAction(this, ColorProfile.DAY));
        addAction(ActionCode.SWITCH_TO_NIGHT_PROFILE, new SwitchProfileAction(this, ColorProfile.NIGHT));

        addAction(ActionCode.EXIT, new ExitAction(this));

        BookTextView = new FBView(this);
        FootnoteView = new FBView(this);

        setView(BookTextView);
    }

    public void setGotoPosition(int paragrphIndex, int wordIndex, int charIndex) {
        gotoPosition = new ZLTextFixedPosition(paragrphIndex, wordIndex, charIndex);
    }

    public void reopenCurrentChapter() {
        readerActivity.reopenCurrentChapter();
    }

    public int getCurrentChapterIndex() {
        return Model.Book.getCurrentChapterIndex();
    }

    //打开一个章节
    public void openChapter(final String chapterId, final int openStyle) {
        final Activity activity = ((ZLAndroidLibrary) ZLAndroidLibrary.Instance()).getActivity();
        if (activity != null) {
            activity.runOnUiThread(new Runnable() {
                public void run() {
                    openChapterInternal(chapterId, openStyle);
                }
            });
        }
    }

    public void openBook(final Book book, final int openStyle) {
        final Activity activity = ((ZLAndroidLibrary) ZLAndroidLibrary.Instance()).getActivity();
        Thread runner = new Thread() {
            public void run() {
                openBookInternal(book, false);
                if (book != null) {
                    book.addLabel(Book.READ_LABEL);
                    Collection.saveBook(book, false);
                }
                if (activity != null) {
                    activity.runOnUiThread(new Runnable() {
                        public void run() {
                            readerActivity.openInitChapter(openStyle);
                        }
                    });
                }
            }
        };
        runner.start();
    }

    public void reloadBook() {
        if (Model != null && Model.Book != null) {
            openMyBook(new Runnable() {
                public void run() {
                    openBookInternal(Model.Book, true);
                    String myChapterId = Model.Book.getChapterId();
                    if (Util.isStringEmpty(myChapterId)) {
                        List<TOCItem> catalog = Model.Book.getCatalogList();
                        if (catalog != null && catalog.size() > 0) {
                            myChapterId = catalog.get(0).getId();
                        }
                    }
                    openChapter(myChapterId, 3);
                }
            });
        }
    }

    private ColorProfile myColorProfile;

    public ColorProfile getColorProfile() {
        if (myColorProfile == null) {
            myColorProfile = ColorProfile.get(getColorProfileName());
        }
        return myColorProfile;
    }

    public void cancelGeneratePagesTask() {
        BookTextView.cancelGeneratePageTask();
    }

    public String getColorProfileName() {
        return ColorProfileOption.getValue();
    }

    public void setColorProfileName(String name) {
        ColorProfileOption.setValue(name);
        myColorProfile = null;
    }

    public ZLKeyBindings keyBindings() {
        return myBindings;
    }

    public FBView getTextView() {
        return (FBView) getCurrentView();
    }

    public void tryOpenFootnote(String id) {
        if (Model != null) {
            myJumpEndPosition = null;
            myJumpTimeStamp = null;
            BookModel.Label label = Model.getLabel(id);
            if (label != null) {
                if (label.ModelId == null) {
                    if (getTextView() == BookTextView) {
                        addInvisibleBookmark();
                        myJumpEndPosition = new ZLTextFixedPosition(label.ParagraphIndex, 0, 0);
                        myJumpTimeStamp = new Date();
                    }
                    BookTextView.gotoPosition(label.ParagraphIndex, 0, 0);
                    setView(BookTextView);
                } else {
                    setFootnoteModel(label.ModelId);
                    setView(FootnoteView);
                    FootnoteView.gotoPosition(label.ParagraphIndex, 0, 0);
                }
                getViewWidget().repaint();
            }
        }
    }

    public void clearTextCaches() {
        BookTextView.clearCaches();
        FootnoteView.clearCaches();
    }

    private void setBookmarkHighlightings(ZLTextView view, String modelId) {
        view.removeHighlightings(BookmarkHighlighting.class);
        String currentChapterId = Model.Book.getChapterId();
        for (BookmarkQuery query = new BookmarkQuery(Model.Book, 20); ; query = query.next()) {
            final List<Bookmark> bookmarks = Collection.bookmarks(query);
            if (bookmarks.isEmpty()) {
                break;
            }
            for (Bookmark b : bookmarks) {
                if (currentChapterId.equalsIgnoreCase(b.getChapterID())) {
                    if (b.getEnd() == null) {
                        b.findEnd(view);
                    }
                    if (b.getMyTypeID() != 1) {
                        view.addHighlighting(new BookmarkHighlighting(view, Collection, b));
                    }

                    if (b.getMyTypeID() == 3) {
                        ZLTextPosition zlTextPosition = b.getEnd();
                        view.addRemarkIconsPosition(b, zlTextPosition);
                    }
                }
            }
        }
    }

    /**
     * 删除笔记后，去掉笔记的icon
     *
     * @param bookmark
     */
    public void deleteRemarkIcon(Bookmark bookmark) {
        if (bookmark.getStyleId() == 3) BookTextView.removeRemarkIconsPosition(bookmark);
    }

    private void setFootnoteModel(String modelId) {
        final ZLTextModel model = Model.getFootnoteModel(modelId);
        FootnoteView.setModel(model);
        if (model != null) {
            myFootnoteModelId = modelId;
            setBookmarkHighlightings(FootnoteView, modelId);
        }
    }

    /**
     * 打开一本书的第三步
     *
     * @param book
     * @param bookmark
     * @param force
     * @param openStyle 1为第一页， 2 为最后一页 ， 3为 最近阅读的地方
     */
    public synchronized void openBookInternal(Book book, boolean force) {
        //#debug
        FootnoteView.setModel(null);
        clearTextCaches();

        Model = null;
        System.gc();
        System.gc();
        try {
            Model = BookModel.createModel(book);
            ZLTextHyphenator.Instance().load(book.getLanguage());
            readerActivity.setCatalogList(book.getCatalogList());
            String title = book.getTitle();
            if (Util.isEmpty(title)) {
                book.setTitle(chapterName);
            }
        } catch (BookReadingException e) {
            processException(e);
        }
    }

    public synchronized void openChapterInternal(String chapterId, int openStyle) {
        onViewChanged();
        clearTextCaches();
        try {
            this.chapterId = chapterId;
            Book book = Model.Book;
            Model.setCurrentChapterid(chapterId);
            //这么写是为了调式
            //ZLTextWritablePlainModel textModel = (ZLTextWritablePlainModel) Model.getTextModel();
            BookTextView.setContext(readerActivity.getInitPaintContext());
            ZLTextModel textModel = Model.getTextModel();
            if (textModel == null||textModel.getParagraphsNumber() == 0) {
                book.getPlugin().readChapter(Model, chapterId);
                textModel = Model.getTextModel();
            }
            BookTextView.setModel(textModel);
            book.setChapterId(chapterId);
            Collection.saveBook(book, true);
            setBookmarkHighlightings(BookTextView, null);

            List<TOCItem> catalogs = book.getCatalogList();
            if (catalogs != null && catalogs.size() > 0) {
                for (TOCItem item : catalogs) {
                    String myChapterId = item.getId();
                    if (chapterId.equalsIgnoreCase(myChapterId)) {
                        chapterName = item.getNavLabel();
                        break;
                    }
                }
            } else {
                chapterName = book.getTitle();
                chapterName = chapterName.substring(0, chapterName.lastIndexOf("."));
            }

            if (openStyle == 1) {
                BookTextView.gotoHome(); //第一页
            } else if (openStyle == 2) {
                BookTextView.gotoLastPage(); //最后一页
            } else if (openStyle == 3) {//打开到最近阅读的地方
                ZLTextPosition recentPoint = Collection.getStoredPosition(book.getId(), chapterId);
                if (recentPoint != null) {
                    BookTextView.gotoPosition(recentPoint);
                } else {
                    BookTextView.gotoHome();
                }
            } else if (openStyle == 4) {//打开到选择的某个点
                if (gotoPosition != null) {
                    BookTextView.gotoPosition(gotoPosition);
                } else {
                    BookTextView.gotoHome();
                }
            } else {
                //其它情况下，打开到第一页
                BookTextView.gotoHome();
            }
        } catch (Exception e) {
            processException(e);
        }
        //触发绘制
        getViewWidget().reset();
        getViewWidget().repaint();
    }

    private List<Bookmark> invisibleBookmarks() {
        final List<Bookmark> bookmarks = Collection.bookmarks(new BookmarkQuery(Model.Book, false, 10));
        Collections.sort(bookmarks, new Bookmark.ByTimeComparator());
        return bookmarks;
    }

    public boolean jumpBack() {
        try {
            if (getTextView() != BookTextView) {
                showBookTextView();
                return true;
            }

            if (myJumpEndPosition == null || myJumpTimeStamp == null) {
                return false;
            }
            // more than 2 minutes ago
            if (myJumpTimeStamp.getTime() + 2 * 60 * 1000 < new Date().getTime()) {
                return false;
            }
            if (!myJumpEndPosition.equals(BookTextView.getStartCursor())) {
                return false;
            }

            final List<Bookmark> bookmarks = invisibleBookmarks();
            if (bookmarks.isEmpty()) {
                return false;
            }
            final Bookmark b = bookmarks.get(0);
            Collection.deleteBookmark(b);
            gotoBookmark(b, true);
            return true;
        } finally {
            myJumpEndPosition = null;
            myJumpTimeStamp = null;
        }
    }

    private void gotoBookmark(Bookmark bookmark, boolean exactly) {
        final String modelId = bookmark.ModelId;
        if (modelId == null) {
            addInvisibleBookmark();
            if (exactly) {
                BookTextView.gotoPosition(bookmark);
            } else {
                BookTextView.gotoHighlighting(new BookmarkHighlighting(BookTextView, Collection, bookmark));
            }
            setView(BookTextView);
        } else {
            setFootnoteModel(modelId);
            if (exactly) {
                FootnoteView.gotoPosition(bookmark);
            } else {
                FootnoteView.gotoHighlighting(new BookmarkHighlighting(FootnoteView, Collection, bookmark));
            }
            setView(FootnoteView);
        }
        getViewWidget().repaint();
    }

    public void showBookTextView() {
        setView(BookTextView);
    }

    public void onWindowClosing() {
        cancelGeneratePagesTask();
        storePosition();
    }

    public void storePosition() {
        if (Model != null && Model.Book != null && BookTextView != null) {
            Collection.storePosition(Model.Book.getId(), Model.Book.getChapterId(), BookTextView.getStartCursor());
            Model.Book.setProgress(BookTextView.getProgress());
            Collection.saveBook(Model.Book, false);
        }
    }

    //	public boolean hasCancelActions() {
    //		//return new CancelMenuHelper().getActionsList(Collection).size() > 1;
    //	}

    //	public void runCancelAction(CancelMenuHelper.ActionType type, Bookmark bookmark) {
    //		switch (type) {
    //			case library:
    //				runAction(ActionCode.SHOW_LIBRARY);
    //				break;
    //			case networkLibrary:
    //				runAction(ActionCode.SHOW_NETWORK_LIBRARY);
    //				break;
    //			case previousBook:
    //				openBook(Collection.getRecentBook(1), null, null);
    //				break;
    //			case returnTo:
    //				Collection.deleteBookmark(bookmark);
    //				gotoBookmark(bookmark, true);
    //				break;
    //			case close:
    //				closeWindow();
    //				break;
    //		}
    //	}

    private synchronized void updateInvisibleBookmarksList(Bookmark b) {
        if (Model != null && Model.Book != null && b != null) {
            for (Bookmark bm : invisibleBookmarks()) {
                if (b.equals(bm)) {
                    Collection.deleteBookmark(bm);
                }
            }
            Collection.saveBookmark(b);
            final List<Bookmark> bookmarks = invisibleBookmarks();
            for (int i = 3; i < bookmarks.size(); ++i) {
                Collection.deleteBookmark(bookmarks.get(i));
            }
        }
    }

    public void addInvisibleBookmark(ZLTextWordCursor cursor) {
        if (cursor != null && Model != null && Model.Book != null && getTextView() == BookTextView) {
            updateInvisibleBookmarksList(Bookmark.createBookmark(Model.Book, getTextView().getModel().getId(), cursor, 6, false, contentId, chapterId, 1));
        }
    }

    public void addInvisibleBookmark() {
        if (Model.Book != null && getTextView() == BookTextView) {
            updateInvisibleBookmarksList(createBookmark(6, false, contentId));
        }
    }

    public Bookmark createBookmark(int maxLength, boolean visible, String contentId) {
        final FBView view = getTextView();
        final ZLTextWordCursor cursor = view.getStartCursor();

        if (cursor.isNull()) {
            return null;
        }

        return Bookmark.createBookmark(Model.Book, view.getModel().getId(), cursor, maxLength, visible, contentId, chapterId, 1);
    }

    public TOCTree getCurrentTOCElement() {
        final ZLTextWordCursor cursor = BookTextView.getStartCursor();
        if (Model == null || cursor == null) {
            return null;
        }

        int index = cursor.getParagraphIndex();
        if (cursor.isEndOfParagraph()) {
            ++index;
        }
        TOCTree treeToSelect = null;
        for (TOCTree tree : Model.TOCTree) {
            final TOCTree.Reference reference = tree.getReference();
            if (reference == null) {
                continue;
            }
            if (reference.ParagraphIndex > index) {
                break;
            }
            treeToSelect = tree;
        }
        return treeToSelect;
    }

    public void onBookUpdated(Book book) {
        if (Model == null || Model.Book == null || !Model.Book.equals(book)) {
            return;
        }

        final String newEncoding = book.getEncodingNoDetection();
        final String oldEncoding = Model.Book.getEncodingNoDetection();

        Model.Book.updateFrom(book);

        if (newEncoding != null && !newEncoding.equals(oldEncoding)) {
            reloadBook();
        } else {
            ZLTextHyphenator.Instance().load(Model.Book.getLanguage());
            clearTextCaches();
            getViewWidget().repaint();
        }
    }

    public boolean isMenuShowing() {
        return isMenuShowing;
    }

    public void setMenuShowing(boolean isMenuShowing) {
        this.isMenuShowing = isMenuShowing;
    }

    private void openMyBook(final Runnable action) {
        final Activity activity = ((ZLAndroidLibrary) ZLAndroidLibrary.Instance()).getActivity();
        if (activity != null) {
            activity.runOnUiThread(new Runnable() {
                public void run() {

                    final Thread runner = new Thread() {
                        public void run() {
                            action.run();
                        }
                    };
                    runner.start();
                }
            });
        } else {
            action.run();
        }
    }

    public void setReaderActivity(ReadingBookActivity readerActivity) {
        this.readerActivity = readerActivity;
    }

    public void setDontCloseLoading(boolean dont) {
        dontCloseLoading = dont;
    }

    protected void OnDrawFinished() {
        if (readerActivity != null && !dontCloseLoading) {
            readerActivity.closeLoading();
        }
        dontCloseLoading = false;
    }

}
